package cn.campus.service.article.impl;

import cn.campus.constant.article.FileConstant;
import cn.campus.converter.ArticleDTOToArticleConverter;
import cn.campus.converter.ArticleToArticleDTOConverter;
import cn.campus.dao.article.ArticleMapper;
import cn.campus.enums.article.ResultEnum;
import cn.campus.exception.article.ArticleException;
import cn.campus.pojo.article.dto.ArticleDTO;
import cn.campus.pojo.article.dto.PageInfo;
import cn.campus.pojo.article.po.Article;
import cn.campus.service.UserService;
import cn.campus.service.article.ArticleService;
import cn.campus.util.KeyUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.ContextLoader;

import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


/**
 * @Description: 文章服务接口实现类
 * @Author: linyanbin
 * @Date: 2018/9/16 0016 16:32
 */
@Service
@Slf4j
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private UserService userService;

    @Override
    @Transactional
    public ArticleDTO save(ArticleDTO articleDTO) {
        Article article = ArticleDTOToArticleConverter.convert(articleDTO);
        if (article.getArticleId() != null) {
            int effectedNum = articleMapper.updateArticle(article);
            if (effectedNum <= 0) {
                log.error("【更新文章】更新失败：articleId = {}", article.getArticleId());
                throw new ArticleException(ResultEnum.UPDATE_ARTICLE_FAIL);
            }
            return articleDTO;
        } else {
            article.setArticleId(KeyUtils.getUUID());
            int effectedNum = articleMapper.insertArticle(article);
            if (effectedNum <= 0) {
                log.error("【添加文章】添加失败：article = {}", article);
                throw new ArticleException(ResultEnum.ADD_ARTICLE_FAIL);
            }
            articleDTO.setArticleId(article.getArticleId());
            return articleDTO;
        }
    }

    @Autowired
    CacheManager cacheManager;

    @Override
    // 如果间隔访问时间小于1分钟，则走缓存，如果大于或等于1分钟，则执行方法并刷新缓存
    @Caching(cacheable = @Cacheable(key = "#articleId", value = "viewCache", condition = "#intervalVisitTime < 60000"),
             put = @CachePut(key = "#articleId", value = "viewCache", condition = "#intervalVisitTime >= 60000"))
    public ArticleDTO findById(String articleId, long intervalVisitTime) {
        // 查找文章
        ArticleDTO articleDTO = articleMapper.unionFindArticleById(articleId);
        if (articleDTO == null) {
            log.error("【查询文章】文章不存在，articleId={}", articleId);
            throw new ArticleException(ResultEnum.ARTICLE_NOT_EXIST);
        }
        // 启动一线程读取存储文章内容的文件
        ServletContext servletContext = ContextLoader.getCurrentWebApplicationContext().getServletContext();
        String realPath = servletContext.getRealPath(FileConstant.WEB_INF + articleDTO.getArticleContent());
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        Future<String> result = executorService.submit(() -> FileUtils.readFileToString(new File(realPath)));

        // 文章访问量加1
        int viewCount = articleDTO.getArticleViewCount() + 1;
        articleMapper.updateViewCount(articleId, viewCount);
        articleDTO.setArticleViewCount(viewCount);

        String content = "";
        try {
            // 获取读取文件的内容
            content = result.get();
        } catch (InterruptedException e) {
            result.cancel(true);
            Thread.currentThread().interrupt();
            log.error("【查询文章】读取文件失败，{}", e);
        } catch (ExecutionException e) {
            result.cancel(true);
            log.error("【查询文章】读取文件失败，{}", e);
        }finally {
            executorService.shutdown();
        }
        articleDTO.setArticleContent(content);
        return articleDTO;
    }

    @Override
    public Article findById(String articleId) {
        return articleMapper.findArticleById(articleId);
    }

    @Override
    @Transactional
    public void deleteById(String articleId) {
        Article article = articleMapper.findArticleById(articleId);
        if (article == null) {
            log.error("【删除文章】文章不存在，articleId={}", articleId);
            throw new ArticleException(ResultEnum.ARTICLE_NOT_EXIST);
        }
        int effectedNum = articleMapper.deleteArticleById(articleId);
        if (effectedNum <= 0) {
            log.error("【删除文章】删除文章数据失败，articleId={}", articleId);
            throw new ArticleException(ResultEnum.REMOVE_ARTICLE_FAIL);
        }else {
            ServletContext servletContext = ContextLoader.getCurrentWebApplicationContext().getServletContext();
            String imgPath = servletContext.getRealPath(article.getNavgationImg());
            String contentFilePath = servletContext.getRealPath(article.getArticleContent());
            try {
                Files.deleteIfExists(Paths.get(imgPath));
                Files.deleteIfExists(Paths.get(contentFilePath));
            } catch (IOException e) {
                log.error("【删除文章】文章相关文件删除失败, imgPath={}, contentFilePath={}", imgPath, contentFilePath);
                throw new ArticleException(ResultEnum.REMOVE_ARTICLE_FAIL);
            }
        }
    }

    @Override
    @Transactional
    public void updateStatusById(String articleId, Integer status) {
        int effectedNum = articleMapper.updateStatusById(articleId, status);
        if (effectedNum <= 0) {
            log.error("【更新文章】更新状态失败：articleId = {}, status = {}", articleId, status);
            throw new ArticleException(ResultEnum.UPDATE_ARTICLE_FAIL);
        }
    }

    @Override
    public PageInfo<ArticleDTO> findPage(boolean isIncludeUserInfo, Integer page, Integer size) {
        // 带用户信息，评论数
        List<ArticleDTO> articleDTOList = articleMapper.unionFindList((page-1)*size, size);
        long count = articleMapper.count();
        PageInfo<ArticleDTO> pageInfo = new PageInfo<ArticleDTO>(page, size);
        pageInfo.setList(articleDTOList);
        pageInfo.setCount(count);
        pageInfo.setTotalPage((int)Math.ceil(count*1.0/size));
        return pageInfo;
    }

    @Override
    public List<ArticleDTO> findTopListByViewCount(Integer number) {
        // 不带用户信息，评论数
        List<Article> articleList = articleMapper.findList(0, number);
        return ArticleToArticleDTOConverter.convert(articleList);
    }

    @Override
    public Integer getViewCount(String articleId) {
        return articleMapper.getViewCount(articleId);
    }

}
